/************************************
 * Package: RGcpp					*
 * Authors: Rui Eduardo Gouveia Gil	*
 * Date: 2010						*
 ************************************/
#include "rgcpp/sys/PTYCommand.h"
#ifndef _WIN32
namespace rgcpp
{

PTYCommand::PTYCommand() :
	childpid(0)
{

}

PTYCommand::~PTYCommand()
{
	this->writeLine("exit");
	sleep(1);
	this->kill();
	close(this->master);
}

void PTYCommand::launch(const std::string& command)
{
	if(childpid > 0)
	{
		Throw(IllegalStateException("Already initialized!"));
	}

	switch((childpid = forkpty(&master,NULL,NULL,NULL)))
	{
		case -1:
		{
			Throw(SystemException(std::string("Unable to launch pty channel: ")+strerror(errno)));
		}
		break;

		case 0:
		{
			execl("/bin/sh","sh",NULL);
			exit(0);
		}
		break;

		default:
		{
			sleep(1);
			if(write(this->master,command.c_str(),command.size()) == -1)
			{
				Throw(SystemException(std::string("Unable to write to PTY: ")+strerror(errno)));
			}

			if(write(this->master,"\n",1) == -1)
			{
				Throw(SystemException(std::string("Unable to write to PTY: ")+strerror(errno)));
			}
		}
		break;
	}
}

void PTYCommand::wait()
{
	if(childpid > 0)
	{
		int status = 0;
		do
		{
			if(::waitpid(this->childpid,&status,0) == -1)
			{
				Throw(SystemException(std::string("Error: ")+strerror(errno)));
			}
		} while (!WIFEXITED(status) && !WIFSIGNALED(status));
	}
}

void PTYCommand::kill()
{
	if(childpid > 0)
		::kill(childpid,SIGKILL);

	childpid = 0;
}

std::string PTYCommand::readLine()
{
	if(childpid <= 0)
	{
		Throw(IllegalStateException("PTY channel need to be initialized!"));
	}

	char next;
	std::stringstream stream;
	while(true)
	{
		if(!stream.str().empty())
		{
			fd_set fdList;

			FD_ZERO(&fdList);
			FD_SET(this->master,&fdList);

			int value;
			struct timeval timeout;
			timeout.tv_sec = 1;
			timeout.tv_usec = 0;
			do
			{
				value = ::select(this->master + 1,&fdList,NULL,NULL,&timeout);
			} while(value == -1 && errno == EINTR);

			if(value == 0)
			{
				std::string result = stream.str();
				return result;
			}
		}

		if(read(this->master,&next,1) < 1)
			return "";

		if(next == '\n')
		{
			if(stream.str().empty())
				continue;

			stream << next;

			std::string result = stream.str();
			return result;
		}

		if(next < 32)
			continue;

		stream << next;
	}

	return "";
}

void PTYCommand::writeLine(const std::string& line)
{
	if(childpid <= 0)
	{
		Throw(IllegalStateException("PTY channel need to be initialized!"));
	}

	if(write(this->master,line.c_str(),line.size()) == -1)
	{
		Throw(SystemException(std::string("Unable to write to PTY: ")+strerror(errno)));
	}

	if(write(this->master,"\n",1) == -1)
	{
		Throw(SystemException(std::string("Unable to write to PTY: ")+strerror(errno)));
	}
}

}
#endif
